/*
 libelm.c
 Copyright (C) 2011 yellow wood goblin carpfish

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef USING_LIBELM

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#include <wctype.h>

#include <ff.h>
#include <diskio.h>
#include <elm.h>
#include <elm_internals.h>

#pragma import __use_utf8_ctype
//#pragma import __use_sjis_ctype

int elm_error;

#define ELM_NAND 0
#define ELM_SD   1
static FATFS _elm[2];
#define VALID_DISK(disk) (disk==ELM_NAND||disk==ELM_SD)

typedef struct __DIR {
	DIREX dir;
	TCHAR name[_MAX_LFN+1];
	size_t namesize;
	BOOL inUse;
} DIR_EX;

static TCHAR CvtBuf[_MAX_LFN+1];

static const char* _ELM_realpath(const char* path)
{
	if(path&&tolower((unsigned char)path[0])=='f'&&tolower((unsigned char)path[1])=='a'&&tolower((unsigned char)path[2])=='t'&&(path[3]=='0'||path[3]=='1')
			&&path[4]==':'){
		return path+3;
	}
	return path;
}

static TCHAR* _ELM_mbstoucs2(const char* src,size_t* len)
{
	mbstate_t ps= {0 };
	wchar_t tempChar;
	int bytes;
	TCHAR* dst=CvtBuf;
	while(src!='\0'){
		bytes=mbrtowc(&tempChar,src,MB_CUR_MAX,&ps);
		if(bytes>0){
			*dst=(TCHAR)tempChar;
			src+=bytes;
			dst++;
		}else if(bytes==0){
			break;
		}else{
			dst=CvtBuf;
			break;
		}
	}
	*dst='\0';
	if(len) *len=dst-CvtBuf;
	return CvtBuf;
}

static size_t _ELM_ucs2tombs(char* dst,const TCHAR* src)
{
	mbstate_t ps= {0 };
	size_t count=0;
	int bytes;
	char buff[MB_CUR_MAX];
	int i;

	while(*src != '\0'){
		bytes=wcrtomb(buff,*src,&ps);
		if(bytes<0){
			return (size_t)-1;
		}
		if(bytes>0){
			for(i=0;i<bytes;i++){
				*dst++=buff[i];
			}
			src++;
			count+=bytes;
		}else{
			break;
		}
	}
	*dst=L'\0';
	return count;
}

int _ELM_errnoparse(int suc,int fail)
{
	int ret=fail;
	switch(elm_error){
		case FR_OK:
			ret=suc;
			break;
		case FR_NO_FILE:
			elm_error=ENOENT;
			break;
		case FR_NO_PATH:
			elm_error=ENOENT;
			break;
		case FR_INVALID_NAME:
			elm_error=EINVAL;
			break;
		case FR_INVALID_DRIVE:
			elm_error=EINVAL;
			break;
		case FR_EXIST:
			elm_error=EEXIST;
			break;
		case FR_DENIED:
			elm_error=EACCES;
			break;
		case FR_NOT_READY:
			elm_error=ENODEV;
			break;
		case FR_WRITE_PROTECTED:
			elm_error=EROFS;
			break;
		case FR_DISK_ERR:
			elm_error=EIO;
			break;
		case FR_INT_ERR:
			elm_error=EIO;
			break;
		case FR_NOT_ENABLED:
			elm_error=EINVAL;
			break;
		case FR_NO_FILESYSTEM:
			elm_error=EIO;
			break;
	}
	return ret;
}

FILE __stdout;
FILE __stdin;
FILE __stderr;

// Files
static FIL openFiles[FOPEN_MAX];

int _ELM_open_r(void* fileStruct,const char* path,int flags,int mode)
{
	FIL* fp=(FIL*)fileStruct;
	BYTE m=0;
	BOOL truncate=FALSE;
	const TCHAR* p=_ELM_mbstoucs2(_ELM_realpath(path),NULL);
	if(flags&O_WRONLY){
		m|=FA_WRITE;
	}else if(flags&O_RDWR){
		m|=FA_READ|FA_WRITE;
	}else{
		m|=FA_READ;
	}
	if(flags&O_CREAT){
		if(flags&O_EXCL)
			m|=FA_CREATE_NEW;
		else if(flags&O_TRUNC)
			m|=FA_CREATE_ALWAYS;
		else
			m|=FA_OPEN_ALWAYS;
	}else{
		if(flags&O_TRUNC) truncate=TRUE;
		m|=FA_OPEN_EXISTING;
	}
	elm_error=f_open(fp,p,m);
	if(elm_error==FR_OK&&truncate){
		elm_error=f_truncate(fp);
	}
	if(elm_error==FR_OK&&(flags&O_APPEND)){
		elm_error=f_lseek(fp,fp->fsize);
	}
	return _ELM_errnoparse((int)fp,-1);
}

FILE* fopen(const char *path,const char * mode)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);

	int fileNum, flags;
	FIL* fp= NULL;

	// Find a free file buffer
	for(fileNum = 0; (fileNum < FOPEN_MAX) && (openFiles[fileNum].inUse == TRUE);fileNum++);

	if(fileNum == FOPEN_MAX){ // No free files
		elm_error = EMFILE;
		return NULL;
	}

	fp = &openFiles[fileNum];

	register int m, o;

	switch(mode[0]){
		case 'r': /* open for reading */
			m = O_RDONLY;
			o = 0;
			break;
		case 'w': /* open for writing */
			m = O_WRONLY;
			o = O_CREAT | O_TRUNC;
			break;
		case 'a': /* open for appending */
			m = O_WRONLY;
			o = O_CREAT | O_APPEND;
			break;
		default: /* illegal mode */
			elm_error = EINVAL;
			return NULL;
	}
	if(mode[1] && (mode[1] == '+' || mode[2] == '+')){
		m = O_RDWR;
	}
	if(mode[1] && (mode[1] == 'b' || mode[2] == 'b')){
#ifdef O_BINARY
		m |= O_BINARY;
#endif
	}

	flags = m | o;

	if(_ELM_open_r(fp,path,flags,0666) == -1){
		return NULL;
	}

	return (FILE*) fp;
}

FILE *freopen(const char *path, const char *mode, FILE* fd) {
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
	int flags;
	FIL* fp=(FIL*)fd;
	
	register int m, o;
		
	switch (mode[0])
	{
		case 'r':			/* open for reading */
			m = O_RDONLY;
			o = 0;
			break;
		case 'w':			/* open for writing */
			m = O_WRONLY;
			o = O_CREAT | O_TRUNC;
			break;
		case 'a':			/* open for appending */
			m = O_WRONLY;
			o = O_CREAT | O_APPEND;
			break;
		default:			/* illegal mode */
			elm_error = EINVAL;
			fclose(fd);
			return NULL;
	}
	if (mode[1] && (mode[1] == '+' || mode[2] == '+'))
	{
		m = O_RDWR;
	}
	if (mode[1] && (mode[1] == 'b' || mode[2] == 'b'))
	{
#ifdef O_BINARY
		m |= O_BINARY;
#endif
	}
		
	flags = m | o;
	
	if(_ELM_open_r(fp,path,flags,0666) == -1){
		return NULL;
	}
	
	return (FILE*) fp;
}

int fclose(FILE* fd)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
	FIL* fp=(FIL*)fd;
	elm_error=f_close(fp);
	return _ELM_errnoparse(0,-1);
}

size_t fwrite(const void * ptr, size_t size, size_t nmemb, FILE * fd) {
	//printf("Called %s(%s): %s(%d)\n",__FUNCTION__,(const char *)ptr,__MODULE__,__LINE__);
	if(fd == stdout || fd == stderr) {
		const char *data;               /* point to where data comes from next */
		size_t total;                   /* total bytes to write */
		size_t count;                   /* num bytes left to write */
		int c;                          /* a temp char */

		data = ptr;
		count = total = size * nmemb;
		
		while (count != 0) {
			c = *data;  /* fputc write one char, this is it */
			if (fputc(c, fd) == EOF) {
				/* error or eof, stream flags set by _flsbuf */
				return (total - count) / size;
			}
			
			/* fputc wrote a char -- update count */
			++data;
			--count;
		}
		
		/* we finished successfully, so just return nmemb */
		return nmemb;
	}
	
#if !_FS_READONLY
	FIL* fp=(FIL*)fd;
	unsigned int written;
	size_t len = size * nmemb;
	elm_error=f_write(fp,ptr,len,&written);
	return _ELM_errnoparse(written,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

size_t fread(void * ptr, size_t size, size_t nmemb, FILE * fd)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
	FIL* fp=(FIL*)fd;
	unsigned int read;
	size_t len = size * nmemb;
	elm_error=f_read(fp,ptr,len,&read);
	return _ELM_errnoparse(read,-1);
}

size_t __fread_bytes_avail(void* ptr,size_t len, FILE * fd)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
	FIL* fp=(FIL*)fd;
	unsigned int read;
	elm_error=f_read(fp,ptr,len,&read);
	return _ELM_errnoparse(read,-1);
}

int fseek (FILE* fd, long int pos, int dir) {
	//printf("Called %s(%d,%d): %s(%d)\n",__FUNCTION__,pos,dir,__MODULE__,__LINE__);

#if _FS_MINIMIZE < 3
	FIL* fp=(FIL*)fd;
	int off=0;
	switch(dir){
		case SEEK_SET:
			off=pos;
			break;
		case SEEK_END:
			off=fp->fsize-pos;
			break;
		case SEEK_CUR:
			off=fp->fptr+pos;
			break;
	}
	if(off > fp->fsize) off=fp->fsize;
	elm_error=f_lseek(fp,off);
	return _ELM_errnoparse(fp->fptr,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

long int ftell(FILE * fd)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	if(fd!=NULL) {
		FIL* fp=(FIL*)fd;
		if(fp->fs != 0)
			return f_tell(fp);
	}
	
	elm_error=FR_INVALID_OBJECT;
	return -1;
}	

void clearerr(FILE * fd)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	//FIL* fp=(FIL*)fd;
	//Not need clear!
}

int feof(FILE * fd)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	if(fd!=NULL) {
		FIL* fp=(FIL*)fd;
		if(fp->fs != 0)
			return f_eof(fp);
	}
	
	elm_error=FR_INVALID_OBJECT;
	return 1;
}

int ferror(FILE * fd)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	FIL* fp=(FIL*)fd;

	if(fp != NULL)
		return f_error(fp);
	
	elm_error=FR_INVALID_OBJECT;
	return 1;
}

static time_t _ELM_filetime_to_time(uint16_t t,uint16_t d)
{
	struct tm timeParts;

	timeParts.tm_hour=t>>11;
	timeParts.tm_min=(t>>5)&0x3f;
	timeParts.tm_sec=(t&0x1f)<<1;

	timeParts.tm_mday=d&0x1f;
	timeParts.tm_mon=((d>>5)&0x0f)-1;
	timeParts.tm_year=(d>>9)+80;

	timeParts.tm_isdst=0;

	return mktime(&timeParts);
}

static void _ELM_fileinfo_to_stat(const TCHAR* path,const FILINFO* fi,struct stat* st)
{
	memset(st,0,sizeof(*st));
	st->st_mode=(fi->fattrib&AM_DIR)?S_IFDIR:0;
	st->st_nlink=1;
	st->st_uid=1;
	st->st_gid=2;
	st->st_rdev=st->st_dev;
	st->st_size=fi->fsize;
	st->st_mtime=_ELM_filetime_to_time(fi->ftime,fi->fdate);
	st->st_spare1=fi->fattrib;
}

static int _ELM_chk_mounted(int disk)
{
	if(VALID_DISK(disk)){
		if(!_elm[disk].fs_type){
			FILINFO fi;
			TCHAR path[3];
			const TCHAR* path_ptr=path;
			path[0]=L'0'+disk;
			path[1]=L':'; path[2]=0;
			f_stat(path_ptr,&fi);
		}
		return _elm[disk].fs_type;
	}
	return FALSE;
}

static void _ELM_disk_to_stat(int disk,struct stat* st)
{
	memset(st,0,sizeof(*st));
	if(_ELM_chk_mounted(disk)){
		FATFS* fs=&(_elm[disk]);
		st->st_dev=(INT)fs;
		st->st_ino=fs->dirbase;
		st->st_mode=S_IFDIR;
		st->st_nlink=1;
		st->st_uid=1;
		st->st_gid=2;
		st->st_rdev=st->st_dev;
	}
}

int stat(const char* file,struct stat* st)
{
	//printf("Called %s(%s): %s(%d)\n",__FUNCTION__,file,__MODULE__,__LINE__);
	
#if _FS_MINIMIZE < 1
	size_t len=0;
	TCHAR* p=_ELM_mbstoucs2(_ELM_realpath(file),&len);
	if(p[len-1]==L'/') p[len-1]=L'\0';
	if((p[0]==L'0'||p[0]==L'1')&&p[1]==L':'&&p[2]==L'\0')
	{
		_ELM_disk_to_stat(p[0]-L'0',st);
		return 0;
	}
	FILINFO fi;
	TCHAR buffer[_MAX_LFN+1];
	fi.lfname=buffer;
	fi.lfsize=sizeof(buffer)/sizeof(buffer[0]);
	elm_error=f_stat(p,&fi);
	if(elm_error!=FR_OK) printf("stat errno= %d\n",elm_error);
	_ELM_fileinfo_to_stat(p,&fi,st);
	return _ELM_errnoparse(0,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

int remove (const char *path)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if (_FS_MINIMIZE < 1) && (!_FS_READONLY)
	const TCHAR* p=_ELM_mbstoucs2(_ELM_realpath(path),NULL);
	elm_error=f_unlink(p);
	return _ELM_errnoparse(0,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

int chdir (const char* path)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if _FS_RPATH
	const TCHAR* p=_ELM_mbstoucs2(_ELM_realpath(path),NULL);
	elm_error=f_chdir(p);
	return _ELM_errnoparse(0,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

int rename (const char* path,const char* pathp)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if (_FS_MINIMIZE < 1) && (!_FS_READONLY)
	TCHAR p[_MAX_LFN+1];
	const TCHAR* pp;
	memcpy(p,_ELM_mbstoucs2(_ELM_realpath(path),NULL),sizeof(p));
	pp=_ELM_mbstoucs2(_ELM_realpath(pathp),NULL);
	if((pp[0]==L'0'||pp[0]==L'1')&&pp[1]==L':') pp+=2;
	elm_error=f_rename(p,pp);
	return _ELM_errnoparse(0,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

int mkdir (const char *path,int mode)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if (_FS_MINIMIZE < 1) && (!_FS_READONLY)
	const TCHAR* p=_ELM_mbstoucs2(_ELM_realpath(path),NULL);
	elm_error=f_mkdir(p);
	return _ELM_errnoparse(0,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

int chmod (const char *path,int mode)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if (_FS_MINIMIZE < 1) && (!_FS_READONLY)
	const TCHAR* p=_ELM_mbstoucs2(_ELM_realpath(path),NULL);
	elm_error=f_chmod(p, mode, AM_RDO|AM_HID|AM_SYS|AM_ARC);
	return _ELM_errnoparse(0,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

// Directorys
static DIR_EX openDirs[FOPEN_MAX];

DIR *opendir(const char *dirname) 
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if _FS_MINIMIZE < 2
	int dirNum;
	DIR *dirp;
	size_t len=0;
	TCHAR* p=_ELM_mbstoucs2(_ELM_realpath(dirname),&len);
	if(p[len-1]==L'/')
	{
		p[len-1]=L'\0';
		--len;
	}
	
	// Find a free Directory buffer
	for (dirNum = 0; (dirNum < FOPEN_MAX) && (openDirs[dirNum].inUse==TRUE); dirNum++);
		
	if (dirNum == FOPEN_MAX) {	// No free Directorys
		elm_error = EMFILE;
		return NULL;
	}

	DIR_EX* dir = &openDirs[dirNum];
	
	if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
		elm_error = ENOMEM;
		return NULL;
	}
	
	memcpy(dir->name,(void*)p,(len+1)*sizeof(TCHAR));
	dir->namesize=len+1;
	elm_error=f_opendir(&(dir->dir),p);
	if(elm_error!=FR_OK) {
		(void)free((void *)dirp);
		return (DIR*)_ELM_errnoparse((int)dirp,NULL);
	}
	elm_error=f_readdir(&(dir->dir),NULL);
	if(elm_error!=FR_OK) {
		(void)free((void *)dirp);
		return (DIR*)_ELM_errnoparse((int)dirp,NULL);
	}
	
	dir->inUse = TRUE;
	
	dirp->dirData = (int)dir;
	dirp->position = (long int)dir->dir.sect;
	dirp->fileData.d_ino = (int)dir->dir.index;
	memset(dirp->fileData.d_name,0,_MAX_LFN);
		
	return (DIR*)_ELM_errnoparse((int)dirp,NULL);
#else
	elm_error=ENOSYS;
	return 0;
#endif
}
	
void rewinddir(DIR *dirp)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if _FS_MINIMIZE < 2
	DIR_EX* dir=(DIR_EX*)(dirp->dirData);
	elm_error=f_readdir(&(dir->dir),NULL);
#else
	elm_error=ENOSYS;
#endif
}

int closedir(DIR *dirp) {
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
	DIR_EX* dir = (DIR_EX*) (dirp->dirData);
	
	// We are no longer using this entry
	dir->inUse = FALSE;
	(void)free((void *)dirp);
	
	return _ELM_errnoparse(0,-1);
}

struct dirent *readdir(DIR *dirp) {
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if _FS_MINIMIZE < 2
	FILINFO fi;
	TCHAR buffer[_MAX_LFN+1];
	
	fi.lfname=buffer;
	fi.lfsize=sizeof(buffer)/sizeof(buffer[0]);
	
	DIR_EX* dir=(DIR_EX*)(dirp->dirData);
	
	elm_error=f_readdir(&(dir->dir),&fi);
	if(elm_error!=FR_OK) return NULL;
	
	if(!fi.fname[0]) return NULL;
#ifdef _USE_LFN
	_ELM_ucs2tombs(dirp->fileData.d_name,fi.lfname[0]?fi.lfname:fi.fname);
#else
	strcpy(dirp->fileData.d_name,fi.fname);
#endif
	{
		TCHAR path[_MAX_LFN+1];
		size_t len=0;
		memcpy(path,dir->name,(dir->namesize-1)*sizeof(TCHAR));
		path[dir->namesize-1]=L'/';
		memcpy(path+dir->namesize,_ELM_mbstoucs2(dirp->fileData.d_name,&len),(len+1)*sizeof(TCHAR));
		_ELM_fileinfo_to_stat(path,&fi,&dirp->fileData.d_stat);
	}
	
	return &dirp->fileData;
#else
	elm_error=ENOSYS;
	return NULL;
#endif
}

int statvfs (const char* path,struct statvfs* buf)
{
	elm_error=ENOSYS;
	return -1;
}

int ftruncate (FILE* fd,int len)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
#if (_FS_MINIMIZE < 1) && (!_FS_READONLY)
	FIL* fp=(FIL*)fd;
	int ptr=fp->fptr;
	elm_error=f_lseek(fp,len);
	if(elm_error!=FR_OK) return _ELM_errnoparse(0,-1);
	if(ptr>len) ptr=len;
	elm_error=f_truncate(fp);
	if(elm_error!=FR_OK) return _ELM_errnoparse(0,-2);
	fp->fptr=ptr;
	return 0;
#else
	elm_error=ENOSYS;
	return -1;
#endif
}


int fflush (FILE* fd) {
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);

#if !_FS_READONLY
	elm_error=f_sync((FIL*)fd);
	return _ELM_errnoparse(0,-1);
#else
	elm_error=ENOSYS;
	return -1;
#endif
}

#if _CODE_PAGE == 1
WCHAR ff_convert(WCHAR src,UINT dir)
{
	(void)dir;
	if(src>=0x80) src='+';
	return src;
}

WCHAR ff_wtoupper(WCHAR chr)
{
	return towupper(chr);
}
#endif

int ELM_Mount(void)
{
	//printf("Called %s: %s(%d)\n",__FUNCTION__,__MODULE__,__LINE__);
	
	int ret=FALSE;
	if(f_mount(ELM_NAND,&(_elm[0]))==FR_OK){
		if(_ELM_chk_mounted(ELM_NAND)) 
			ret=TRUE;
	}
	if(f_mount(ELM_SD,&(_elm[1]))==FR_OK){
		if(_ELM_chk_mounted(ELM_SD)) 
			ret=TRUE;;
	}
	
	elm_error=0;
	return ret;
}

void ELM_Unmount(void)
{
	f_mount(ELM_NAND,NULL);
	f_mount(ELM_SD,NULL);
}

int ELM_ClusterSizeFromDisk(int disk,uint32_t* size)
{
	if(_ELM_chk_mounted(disk)){
		*size=_elm[disk].csize*ELM_SS(_elm[disk]);
		return TRUE;
	}
	return FALSE;
}

int ELM_ClustersFromDisk(int disk,uint32_t* clusters)
{
	if(_ELM_chk_mounted(disk)){
		*clusters=_elm[disk].n_fatent-2;
		return TRUE;
	}
	return FALSE;
}

int ELM_FreeClustersFromDisk(int disk,uint32_t* clusters)
{
	if(VALID_DISK(disk)){
		FATFS *fs;
		DWORD free_clusters=0;
		TCHAR path[3];
		path[0]=L'0'+disk;
		path[1]=L':'; path[2]=0;
		if(f_getfree(path,&free_clusters,&fs)==FR_OK)
		{
			*clusters=free_clusters;
			return TRUE;
		}
	}
	return FALSE;
}

int ELM_SectorsFromDisk(int disk,uint32_t* sectors)
{
	if(_ELM_chk_mounted(disk)){
		*sectors=(_elm[disk].n_fatent-2)*_elm[disk].csize;
		return TRUE;
	}
	return FALSE;
}

int ELM_ClusterSizeFromHandle(FILE* fd,uint32_t* size)
{
	FIL* fp=(FIL*)fd;
	if(fp->fs != 0)
	{
		*size=fp->fs->csize*ELM_SS(fp->fs);
		return TRUE;
	}
	return FALSE;
}

int ELM_SectorsPerClusterFromHandle(FILE* fd,uint32_t* per)
{
	FIL* fp=(FIL*)fd;
	if(fp->fs != 0)
	{
		*per=fp->fs->csize;
		return TRUE;
	}
	return FALSE;
 }

DWORD get_fat(FATFS* fs,DWORD clst);
DWORD clust2sect(FATFS *fs,DWORD clst);

uint32_t ELM_GetFAT(FILE* fd,uint32_t cluster,uint32_t* sector)
{
	uint32_t result=0;
	FIL* fp=(FIL*)fd;
	if(sector) *sector=0;
	if(fp->fs != 0)
	{
		if(cluster==1)
		{
			result=fp->org_clust;
		}
		else
		{
			result=get_fat(fp->fs,cluster);
			if(result<2||result>=fp->fs->n_fatent) result=0;
		}
		if(sector&&result) *sector=clust2sect(fp->fs,result);
	}
	return result;
}

int ELM_DirEntry(FILE* fd,uint64_t* entry)
{
	FIL* fp=(FIL*)fd;
	if(fp->fs != 0)
	{
		uint64_t value=fp->dir_sect;
		value=value*ELM_SS(fp->fs)+(fp->dir_ptr-fp->fs->win);
		*entry=value;
		return TRUE;
	}
	return FALSE;
}

#endif
